﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DomainDriver.CommonUtilities.Enums
{
    // NOTE: I wish I could use "Enum" instead of struct in constraint
    public static class EnumUtilities
    {
        public static bool IsTypeAnEnum(Type typeToCheck)
        {
            Type enumType = typeof(Enum);
            if (!typeToCheck.IsSubclassOf(enumType))
            { return false; }
            return true;
        }

        public static void AssertTypeIsAnEnum(Type typeToCheck)
        {
            if (!IsTypeAnEnum(typeToCheck))
            { throw new ApplicationException(); }
        }

        public static int GetMinEnumValue(Type enumType)
        {
            AssertTypeIsAnEnum(enumType);

            Array enumValues = Enum.GetValues(enumType);
            int minValue = int.MaxValue;

            foreach (int enumValue in enumValues)
            {
                if (enumValue < minValue)
                {
                    minValue = enumValue;
                }
            }
            return minValue;
        }

        public static int GetMaxEnumValue(Type enumType)
        {
            AssertTypeIsAnEnum(enumType);

            Array enumValues = Enum.GetValues(enumType);
            int maxValue = int.MinValue;

            foreach (int enumValue in enumValues)
            {
                if (enumValue > maxValue)
                {
                    maxValue = enumValue;
                }
            }
            return maxValue;
        }

        public static bool IsTypeAnEnum<TYPE_TO_CHECK>()
            where TYPE_TO_CHECK : struct, IComparable, IFormattable, IConvertible
        {
            Type typeToCheck = typeof(TYPE_TO_CHECK);
            return IsTypeAnEnum(typeToCheck);
        }

        public static void AssertTypeIsAnEnum<TYPE_TO_CHECK>()
            where TYPE_TO_CHECK : struct, IComparable, IFormattable, IConvertible
        {
            Type typeToCheck = typeof(TYPE_TO_CHECK);
            AssertTypeIsAnEnum(typeToCheck);
        }

        public static int GetMinEnumValue<ENUM_TYPE>()
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            Type enumType = typeof(ENUM_TYPE);
            return GetMinEnumValue(enumType);
        }

        public static int GetMaxEnumValue<ENUM_TYPE>()
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            Type enumType = typeof(ENUM_TYPE);
            return GetMaxEnumValue(enumType);
        }

        public static string GetNameOfValue<ENUM_TYPE>(ENUM_TYPE value, bool allowNull)
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            AssertTypeIsAnEnum<ENUM_TYPE>();
            Type enumType = typeof(ENUM_TYPE);
            string name = Enum.GetName(enumType, value);

            if (string.IsNullOrWhiteSpace(name))
            { name = value.ToString(); }

            return name;
        }

        public static string GetNameOfValue<ENUM_TYPE>(ENUM_TYPE value)
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            return GetNameOfValue<ENUM_TYPE>(value, false);
        }

        public static int ConvertEnumToInt<ENUM_TYPE>(ENUM_TYPE enumValue)
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            AssertTypeIsAnEnum<ENUM_TYPE>();
            object objectValue = enumValue;
            int intValue = (int)objectValue;
            return intValue;
        }

        public static ENUM_TYPE ConvertIntToEnum<ENUM_TYPE>(int intValue)
            where ENUM_TYPE : struct, IComparable, IFormattable, IConvertible
        {
            AssertTypeIsAnEnum<ENUM_TYPE>();
            object objectValue = intValue;
            ENUM_TYPE enumValue = (ENUM_TYPE)objectValue;
            return enumValue;
        }
    }
}